package com.ruicar.afs.cloud.basic.common.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruicar.afs.cloud.basic.common.condition.DeductionInfoChildCondition;
import com.ruicar.afs.cloud.basic.common.condition.QueryCustInfoMultipleCondition;
import com.ruicar.afs.cloud.basic.common.entity.*;
import com.ruicar.afs.cloud.basic.common.mapper.BasicRepaymentPlanMapper;
import com.ruicar.afs.cloud.basic.common.service.*;
import com.ruicar.afs.cloud.basic.detail.vo.RentInfoVO;
import com.ruicar.afs.cloud.batch.entity.AfsBatchInfo;
import com.ruicar.afs.cloud.batch.service.AfsBatchInfoService;
import com.ruicar.afs.cloud.common.modules.contract.enums.*;
import com.ruicar.afs.cloud.common.util.EmptyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: Lee
 * @date 2020-05-30 04:35:07
 * @description
 */
@Service
public class BasicRepaymentPlanServiceImpl extends ServiceImpl<BasicRepaymentPlanMapper, BasicRepaymentPlan> implements BasicRepaymentPlanService {
    @Autowired
    private BasicAssetsRepaymentPlanService basicAssetsRepaymentPlanService;
    @Autowired
    private BasicMainInfoService basicMainInfoService;
    @Autowired
    private AfsBatchInfoService afsBatchInfoService;
    @Autowired
    private BasicPenaltyInterestService basicPenaltyInterestService;
    @Autowired
    private BasicPenaltyInterestDetailsService basicPenaltyInterestDetailsService;
    @Autowired
    private BasicFinancialAgreementService basicFinancialAgreementService;
    @Autowired
    private BasicDebitInfoService basicDebitInfoService;
    @Autowired
    private BasicExpenseRecordService basicExpenseRecordService;

    @Override
    public List<RentInfoVO> queryRepaymentInfo(String contractNo, String isOverdue) {
        return baseMapper.queryRepaymentInfo(contractNo, isOverdue);
    }

    @Override
    public List<DeductionInfoChildCondition> deductionInfo(QueryCustInfoMultipleCondition condition) {
        return baseMapper.deductionInfo(condition);
    }

    @Override
    public void handleCapitalForCancel(BasicReceiptDealDetails receiptDealDetails, AfsBatchInfo afsBatchInfo, Date batchDate) {
        BigDecimal dealAmt = receiptDealDetails.getDealAmt();
        BasicRepaymentPlan repaymentPlan = this.getById(receiptDealDetails.getDealId());
        repaymentPlan.setActualPrinciple(repaymentPlan.getActualPrinciple().subtract(dealAmt));
        repaymentPlan.setActualRent(repaymentPlan.getActualRent().subtract(dealAmt));
        repaymentPlan.setActualPaymentDate(null);
        if (repaymentPlan.getDueDate().before(batchDate)) {
            repaymentPlan.setStatus(RepaymentStatusEnum.OVERDUE);
        } else {
            repaymentPlan.setStatus(RepaymentStatusEnum.UNPAID);
        }
        this.updateById(repaymentPlan);
        this.baseMapper.updateActualPaymentDate(repaymentPlan.getContractNo(), repaymentPlan.getTermNo());
        basicAssetsRepaymentPlanService.cancelPrinciple(receiptDealDetails.getId(), repaymentPlan, batchDate);
        BasicMainInfo basicMainInfo = basicMainInfoService.getOne(Wrappers.<BasicMainInfo>query().lambda()
                .eq(BasicMainInfo::getContractNo, receiptDealDetails.getContractNo())
        );
        this.calculatePenaltyInteres(receiptDealDetails.getCreateTime(),repaymentPlan, dealAmt, basicMainInfo, afsBatchInfo,batchDate);

    }

    @Override
    public void handleInterestForCancel(BasicReceiptDealDetails receiptDealDetails, AfsBatchInfo afsBatchInfo, Date batchDate) {
        BigDecimal dealAmt = receiptDealDetails.getDealAmt();
        BasicRepaymentPlan repaymentPlan = this.getById(receiptDealDetails.getDealId());
        repaymentPlan.setActualInterest(repaymentPlan.getActualInterest().subtract(dealAmt));
        repaymentPlan.setActualRent(repaymentPlan.getActualRent().subtract(dealAmt));
        repaymentPlan.setActualPaymentDate(null);
        if (repaymentPlan.getDueDate().before(batchDate)) {
            repaymentPlan.setStatus(RepaymentStatusEnum.OVERDUE);
        } else {
            repaymentPlan.setStatus(RepaymentStatusEnum.UNPAID);
        }
        this.updateById(repaymentPlan);
        this.baseMapper.updateActualPaymentDate(repaymentPlan.getContractNo(), repaymentPlan.getTermNo());
        basicAssetsRepaymentPlanService.cancelInterest(receiptDealDetails.getId(), repaymentPlan, batchDate);
        BasicMainInfo basicMainInfo = basicMainInfoService.getOne(Wrappers.<BasicMainInfo>query().lambda()
                .eq(BasicMainInfo::getContractNo, receiptDealDetails.getContractNo())
        );
        this.calculatePenaltyInteres(receiptDealDetails.getCreateTime(),repaymentPlan, dealAmt, basicMainInfo, afsBatchInfo,batchDate);
    }

    public BasicRepaymentPlan getPlan(String contractNo, Integer termNo) {
        return this.getOne(Wrappers.<BasicRepaymentPlan>lambdaQuery().eq(BasicRepaymentPlan::getContractNo, contractNo).eq(BasicRepaymentPlan::getTermNo, termNo), false);
    }

    @Override
    public BasicRepaymentPlan calculateRepaymentInfo(List<BasicRepaymentPlan> repaymentPlanList) {
        /**  总本金 by ZC.GUO  **/
        BigDecimal totalPrinciple = BigDecimal.ZERO;
        /**  总利息 by ZC.GUO  **/
        BigDecimal totalInterest = BigDecimal.ZERO;
        /**  总租金 by ZC.GUO  **/
        BigDecimal totalRent = BigDecimal.ZERO;
        /**  已还期数 by ZC.GUO  **/
        BigDecimal paidTerms = BigDecimal.ZERO;
        /**  合同最后一次还款日 by ZC.GUO  **/
        Date lastPayDate = null;
        /**  合同首次应还款日 by ZC.GUO  **/
        Date firstPayDate = null;
        /**  合同未偿租金 by ZC.GUO  **/
        BigDecimal unpaidRent = BigDecimal.ZERO;
        /**  上一次还款日 by ZC.GUO  **/
        Date prePayDate = null;

        /**  未偿本金 by ZC.GUO  **/
        BigDecimal totalUnpaidPrinciple = BigDecimal.ZERO;
        /**  未偿利息 by ZC.GUO  **/
        BigDecimal totalUnpaidInterest = BigDecimal.ZERO;

        /**  已偿本金 by ZC.GUO  **/
        BigDecimal paidPrinciple = BigDecimal.ZERO;
        /**  已偿利息 by ZC.GUO  **/
        BigDecimal paidInterest = BigDecimal.ZERO;
        /**  已偿租金 by ZC.GUO  **/
        BigDecimal totalPaidRent = BigDecimal.ZERO;

        BasicRepaymentPlan repaymentPlan = null;
        if (EmptyUtils.isNotEmpty(repaymentPlanList)) {
            /**  按照期数排序 by ZC.GUO  **/
            repaymentPlanList.sort(Comparator.comparing(BasicRepaymentPlan::getTermNo));
            /**  按照期数结构化 by ZC.GUO  **/
            Map<Integer, List<BasicRepaymentPlan>> planMap = repaymentPlanList.stream()
                    .collect(Collectors.groupingBy(BasicRepaymentPlan::getTermNo));
            /**  遍历 by ZC.GUO  **/
            for (BasicRepaymentPlan plan : repaymentPlanList) {
                totalPrinciple = totalPrinciple.add(plan.getReceivablePrinciple());
                totalInterest = totalInterest.add(plan.getReceivableInterest());
                totalRent = totalRent.add(plan.getReceivableRent());

                totalUnpaidPrinciple = totalUnpaidPrinciple.add(plan.getReceivablePrinciple().subtract(EmptyUtils.isNotEmpty(plan.getActualPrinciple()) ? plan.getActualPrinciple() : BigDecimal.ZERO));
                totalUnpaidInterest = totalUnpaidInterest.add(plan.getReceivableInterest().subtract(EmptyUtils.isNotEmpty(plan.getActualInterest()) ? plan.getActualInterest() : BigDecimal.ZERO));

                paidPrinciple = paidPrinciple.add(EmptyUtils.isNotEmpty(plan.getActualPrinciple()) ? plan.getActualPrinciple() : BigDecimal.ZERO);
                paidInterest = paidInterest.add(EmptyUtils.isNotEmpty(plan.getActualInterest()) ? plan.getActualInterest() : BigDecimal.ZERO);
                totalPaidRent = totalPaidRent.add(EmptyUtils.isNotEmpty(plan.getActualRent()) ? plan.getActualRent() : BigDecimal.ZERO);

                if (RepaymentStatusEnum.REPAID.equals(plan.getStatus())) {
                    paidTerms = paidTerms.add(BigDecimal.ONE);
                }
                if (RepaymentStatusEnum.UNPAID.equals(plan.getStatus())
                        || RepaymentStatusEnum.OVERDUE.equals(plan.getStatus())) {
                    unpaidRent = unpaidRent.add(plan.getReceivableRent().subtract(plan.getActualRent()));
                }
                /**  最后一次还款日 by ZC.GUO  **/
                if (EmptyUtils.isEmpty(lastPayDate)) {
                    lastPayDate = plan.getDueDate();
                } else {
                    if (lastPayDate.before(plan.getDueDate())) {
                        lastPayDate = plan.getDueDate();
                    }
                }
                /**  首次应还款日 by ZC.GUO  **/
                if (EmptyUtils.isEmpty(firstPayDate)) {
                    firstPayDate = plan.getDueDate();
                } else {
                    if (firstPayDate.after(plan.getDueDate())) {
                        firstPayDate = plan.getDueDate();
                    }
                }
                /**  上一次还款日期 by ZC.GUO  **/
                if (RepaymentStatusEnum.REPAID.equals(plan.getStatus())) {
                    if (EmptyUtils.isEmpty(prePayDate)) {
                        prePayDate = plan.getActualPaymentDate();
                    } else {
                        if (prePayDate.before(plan.getActualPaymentDate())) {
                            prePayDate = plan.getActualPaymentDate();
                        }
                    }
                }
                /**  当前期数 只考虑最后一期未逾期的情况，最后一期如果逾期则当前期数为空 by ZC.GUO  **/
                /**  应还时间为当前时间之后 by ZC.GUO  **/
                if (EmptyUtils.isEmpty(repaymentPlan) && DateUtil.endOfDay(plan.getDueDate()).after(Calendar.getInstance().getTime())) {
                    /**  若本次循环为第一期，则代表当当前期数为第一期 by ZC.GUO  **/
                    if (plan.getTermNo() == 1) {
                        repaymentPlan = plan;
                    }
                    /**  非第一期，则综合判断前一期的应还日期 by ZC.GUO  **/
                    else {
                        BasicRepaymentPlan lastPlan = planMap.get(plan.getTermNo() - 1).get(0);
                        /**  上一期还款日为当前日期之前 by ZC.GUO  **/
                        if (DateUtil.endOfDay(lastPlan.getDueDate()).before(Calendar.getInstance().getTime())) {
                            repaymentPlan = plan;
                        }
                    }
                }

            }
        }


        if (EmptyUtils.isEmpty(repaymentPlan)) {
            repaymentPlan = new BasicRepaymentPlan();
        }

        repaymentPlan.setTotalPrinciple(totalPrinciple);
        repaymentPlan.setTotalInterest(totalInterest);
        repaymentPlan.setTotalRent(totalRent);
        repaymentPlan.setPaidTerms(paidTerms.intValue());
        repaymentPlan.setLastPayDate(lastPayDate);
        repaymentPlan.setFirstPayDate(firstPayDate);
        repaymentPlan.setUnpaidRent(unpaidRent);
        repaymentPlan.setPrePayDate(prePayDate);
        repaymentPlan.setTotalUnpaidPrinciple(totalUnpaidPrinciple);
        repaymentPlan.setTotalUnpaidInterest(totalUnpaidInterest);
        repaymentPlan.setPaidPrinciple(paidPrinciple);
        repaymentPlan.setPaidInterest(paidInterest);
        repaymentPlan.setTotalRent(totalRent);
        repaymentPlan.setTotalPaidRent(totalPaidRent);
        return repaymentPlan;
    }

    @Override
    public int getOverdueInfo(String contractNo) {
        return baseMapper.getOverdueInfo(contractNo);
    }

    //计算罚息
    public void calculatePenaltyInteres(Date createTime,BasicRepaymentPlan repaymentPlan, BigDecimal amount, BasicMainInfo mainInfo, AfsBatchInfo afsBatchInfo, Date batchDate) {
        if (!RepaymentStatusEnum.OVERDUE.equals(repaymentPlan.getStatus())) {
            return;
        }
        long between = DateUtil.betweenDay(repaymentPlan.getDueDate(), DateUtil.parse(afsBatchInfo.getBatchDate(), DatePattern.PURE_DATE_PATTERN), true);
        int overDueDay = (int) between;

        /**
         * 收款时间与应还款日期相隔天数
         */

        if (createTime.compareTo(repaymentPlan.getDueDate())>0) {
            long days = DateUtil.betweenDay(repaymentPlan.getDueDate(), createTime, true);
            between=between-days;
        }
//    BatchDateInfo batchDateInfo = new BatchDateInfo(DateUtil.parse(afsBatchInfo.getBatchDate()), BatchHelper.isHoliday(afsBatchInfo.getBatchDate()));
        //计算到昨天的罚息
        if (between - 1 <= 0) return;
        BasicFinancialAgreement basicFinancialAgreement = basicFinancialAgreementService.getOne(Wrappers.<BasicFinancialAgreement>lambdaQuery()
                .eq(BasicFinancialAgreement::getContractNo, repaymentPlan.getContractNo())
                .eq(BasicFinancialAgreement::getCostType, AssetTypeEnum.CAR_AMT));
        BigDecimal penaltyInteres = BigDecimal.ZERO;
        penaltyInteres = (amount).multiply(basicFinancialAgreement.getOverDueRate())
                .divide(new BigDecimal("36000"), 2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(between - 1));
        updateDebitOverdueAmt(repaymentPlan, penaltyInteres, overDueDay - 1,mainInfo.getGracePeriodDays());
       if(penaltyInteres.compareTo(BigDecimal.ZERO)==0){
           return;
        }
        //保存罚息明细
        saveBasicPenaltyInterestDetails(repaymentPlan, penaltyInteres,batchDate);
        // 判断是否在宽限期内
        if (between > mainInfo.getGracePeriodDays()) {
            BasicPenaltyInterest basicPenaltyInterest = basicPenaltyInterestService.getPenaltyInterest(repaymentPlan.getContractNo(), repaymentPlan.getTermNo());
             BasicExpenseRecord  basicExpenseRecord = basicExpenseRecordService.getOne(Wrappers.<BasicExpenseRecord>query().lambda()
                    .eq(BasicExpenseRecord::getContractNo, repaymentPlan.getContractNo())
                    .eq(BasicExpenseRecord::getCostType, FeeTypeEnum.penalty),false
            );
             if(basicExpenseRecord!=null){
                 basicExpenseRecord.setCostAmt(basicExpenseRecord.getCostAmt().add(penaltyInteres));
                 basicExpenseRecord.setRemainingAmt((basicExpenseRecord.getRemainingAmt().add(penaltyInteres)));
                 basicExpenseRecord.setStatus(ExpenseStatusEnum.unsettle);
                 basicExpenseRecordService.updateById(basicExpenseRecord);
             }else{
                 basicExpenseRecord =new BasicExpenseRecord();
                 basicExpenseRecord.setCostAmt(penaltyInteres);
                 basicExpenseRecord.setRemainingAmt(penaltyInteres);
                 basicExpenseRecord.setCostType(FeeTypeEnum.penalty);
                 basicExpenseRecord.setStatus(ExpenseStatusEnum.unsettle);
                 basicExpenseRecord.setContractNo(repaymentPlan.getContractNo());
                 basicExpenseRecordService.save(basicExpenseRecord);
             }
            if (basicPenaltyInterest != null) {
                basicPenaltyInterest.setOverdueAmt(basicPenaltyInterest.getOverdueAmt().add(penaltyInteres));
                basicPenaltyInterest.setOverdueDays(overDueDay - 1);
                basicPenaltyInterest.setTotalPenalty((basicPenaltyInterest.getTotalPenalty() != null ? basicPenaltyInterest.getTotalPenalty() : BigDecimal.ZERO).add(penaltyInteres));
                basicPenaltyInterest.setStatus(ExpenseStatusEnum.unsettle);
                basicPenaltyInterest.setOverdueRate(basicFinancialAgreement.getOverDueRate());
                basicPenaltyInterest.setEndDate(batchDate);
                basicPenaltyInterestService.updateById(basicPenaltyInterest);
            } else {
                basicPenaltyInterest =new BasicPenaltyInterest();
                BigDecimal overdueAmt = repaymentPlan.getReceivableRent().add(penaltyInteres).subtract(repaymentPlan.getActualRent());
                basicPenaltyInterest.setOverdueAmt(overdueAmt);
                basicPenaltyInterest.setContractNo(repaymentPlan.getContractNo());
                basicPenaltyInterest.setTermNo(repaymentPlan.getTermNo());
                basicPenaltyInterest.setStartDate(repaymentPlan.getDueDate());
                basicPenaltyInterest.setOverdueDays(overDueDay - 1);
                basicPenaltyInterest.setTotalPenalty(penaltyInteres);
                basicPenaltyInterest.setStatus(ExpenseStatusEnum.unsettle);
                basicPenaltyInterest.setOverdueRate(basicFinancialAgreement.getOverDueRate());
                basicPenaltyInterest.setEndDate(batchDate);
                basicPenaltyInterestService.save(basicPenaltyInterest);
            }
        }


    }

    /**
     * 保存罚息明细信息
     */
    public void saveBasicPenaltyInterestDetails(BasicRepaymentPlan repaymentPlan, BigDecimal amount,Date batchDate) {
        BasicPenaltyInterestDetails penaltyInterestDetails = new BasicPenaltyInterestDetails();
        penaltyInterestDetails.setContractNo(repaymentPlan.getContractNo());
        penaltyInterestDetails.setTermNo(repaymentPlan.getTermNo());
        penaltyInterestDetails.setCostType(FeeTypeEnum.penalty);
        penaltyInterestDetails.setOperateType(CostDetailedTypeEnum.newArrears);
        penaltyInterestDetails.setDate(batchDate);
        penaltyInterestDetails.setAmount(amount);
        basicPenaltyInterestDetailsService.save(penaltyInterestDetails);
    }

    private BigDecimal penaltyInterest(BasicRepaymentPlan repaymentPlan) {
        List<BasicPenaltyInterestDetails> list = basicPenaltyInterestDetailsService.
                list(Wrappers.<BasicPenaltyInterestDetails>query().lambda().eq(BasicPenaltyInterestDetails::getContractNo, repaymentPlan.getContractNo()).
                        eq(BasicPenaltyInterestDetails::getTermNo, repaymentPlan.getTermNo()).eq(BasicPenaltyInterestDetails::getCostType, FeeTypeEnum.penalty)
                );
        BigDecimal amt = BigDecimal.ZERO;
        for (BasicPenaltyInterestDetails basicPenaltyInterestDetails : list) {
            switch (basicPenaltyInterestDetails.getOperateType()) {
                case newArrears:
                    amt = amt.add(basicPenaltyInterestDetails.getAmount() != null ? basicPenaltyInterestDetails.getAmount() : BigDecimal.ZERO);
                    break;
            }
        }
        return amt;
    }

    //更新借据信息
    private void updateDebitOverdueAmt(BasicRepaymentPlan repaymentPlan, BigDecimal amount, int overDueDay,int gracePeriodDays) {
        BasicDebitInfo basicDebitInfo = basicDebitInfoService.getOne(Wrappers.<BasicDebitInfo>lambdaQuery().eq(BasicDebitInfo::getContractNo, repaymentPlan.getContractNo()));
        //计算历史最大逾期天数
        int day = basicDebitInfo.getHistoryBigOverdue() != null ? (basicDebitInfo.getHistoryBigOverdue() >= overDueDay ? basicDebitInfo.getHistoryBigOverdue() : overDueDay) : 0;
        int currentAbsoluteOverdue = basicDebitInfo.getCurrentAbsoluteOverdue() != null ? (basicDebitInfo.getCurrentAbsoluteOverdue() >= overDueDay ? basicDebitInfo.getCurrentAbsoluteOverdue() : overDueDay) : 0;
        /** 当前相对逾期天数 去除宽限期的 */
       if(overDueDay>gracePeriodDays){
           int currentRelativeOverdue = basicDebitInfo.getCurrentRelativeOverdue() != null ? (basicDebitInfo.getCurrentRelativeOverdue() >= overDueDay ? basicDebitInfo.getCurrentRelativeOverdue() : overDueDay) : 0;
           basicDebitInfo.setCurrentRelativeOverdue(currentRelativeOverdue);
           basicDebitInfo.setOverdueTotalAmount((basicDebitInfo.getOverdueTotalAmount() != null ? basicDebitInfo.getOverdueTotalAmount() : BigDecimal.ZERO).add(amount));
       }
        basicDebitInfo.setHistoryBigOverdue(day);
        basicDebitInfo.setCurrentAbsoluteOverdue(currentAbsoluteOverdue);
        basicDebitInfo.setIsOverdue(YesOrNoEnum.yes);
        basicDebitInfoService.updateById(basicDebitInfo);
    }



}
